package pl.ppsoft.fixlang;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Random;

import pl.ppsoft.fixlang.dto.WordDTO;
import pl.ppsoft.fixlang.enums.LearnModeEnum;
import pl.ppsoft.fixlang.enums.LearnStateEnum;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.preference.PreferenceManager;
import android.speech.RecognizerIntent;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

public class LearnActivity extends Activity implements OnInitListener {
	
	private static final int TEXT_TO_SPEECH_CODE = 10;
	private static final int VOICE_RECOGNITION_CODE = 20;

	private static final int EDIT_WORD_DIALOG = 1;	
	
	public static final String LEARN_MODE_PARAM = "LearnMode";
	public static final String LESSONS_PARAM = "Lessons";
	public static final String REPEAT_LEARN = "RepeatLearn";
	public static final String LEARN_CROSS_NUMBER = "LearnCrossNumber";
	public static final String LEARN_RANDOM_LIST = "LearnRandomList";
	
	TextView question;
	TextView answer;
	TextView example;
	EditText writing;
	TextView pronounce;
	ImageButton listening;
	ToggleButton commit;
	TextView counterText;
	TableLayout finish;
	View space;
	
    private DbAdapter db;
    private SharedPreferences settings;
    private TextToSpeech textToSpeech;
	private GestureDetector gestureDetector;
	private LearnModeEnum mode, target;
	private List<LearnModeEnum> randomModes;
	private List<Long> idsToLearn;
	private List<Long> idsTouched = new ArrayList<Long>();
	private Long currentWordId, currentLessonId;
	private int touchedIndex = -1;
	private boolean foreignQuestion;
	private int touched = 0, passed = 0;
	private boolean speechOnLearn = false, speechWriting = false;
	private LearnStateEnum state = LearnStateEnum.QUESTION;
	private Handler handler;
	private Resources res;
	private boolean repeat = false;
	private long historyId;
	
	@SuppressWarnings("unchecked")
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.gestureDetector = new GestureDetector(this, new LearnGestureListener());        
        this.res = getResources();
        
        setContentView(R.layout.learn);		
        counterText = (TextView) findViewById(R.id.counter);
        		        
        db = new DbAdapter(this);
        db.open();
        settings = PreferenceManager.getDefaultSharedPreferences(this);

		Bundle extras = getIntent().getExtras();		
		this.target = this.mode = (LearnModeEnum)extras.getSerializable(LEARN_MODE_PARAM);		
        
        if ( settings.getBoolean(ConfigActivity.SPEECH_PREFERENCE, false) ) {        	
	        Intent checkIntent = new Intent();
	        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
	        startActivityForResult(checkIntent, TEXT_TO_SPEECH_CODE);
	        
	        this.speechOnLearn = settings.getBoolean(ConfigActivity.SPEECH_ONLEARN_PREFERENCE, false);
	        this.speechWriting = settings.getBoolean(ConfigActivity.WRITING_SPEECH_PREFERENCE, false);
        }
	                
		this.question = (TextView)findViewById(R.id.question);
		this.question.setOnLongClickListener( new OnLongClickListener() {

			@Override
			public boolean onLongClick(View v) {
				if ( foreignQuestion && textToSpeech != null) {
					textToSpeech.speak(((TextView)v).getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
				}	
				return true;
			}
			
		});
		
		this.answer = (TextView)findViewById(R.id.answer);
		this.answer.setOnLongClickListener( new OnLongClickListener() {

			@Override
			public boolean onLongClick(View v) {
				if ( !foreignQuestion && textToSpeech != null ) {
					textToSpeech.speak(((TextView)v).getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
				}		
				return true;
			}
			
		});

		this.example = (TextView)findViewById(R.id.example);
		this.commit = (ToggleButton)findViewById(R.id.commit);
		this.writing = (EditText)findViewById(R.id.writing);				
		this.finish = (TableLayout)findViewById(R.id.finish);
		this.space = findViewById(R.id.space);
		
		this.pronounce = (TextView)findViewById(R.id.pronouce);
		this.pronounce.setOnClickListener( new OnClickListener() {

			@Override
			public void onClick(View v) {
		        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
		        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
		        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speech recognition demo");
		        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, settings.getString(ConfigActivity.LOCALE_PREFERENCE, "en_GB"));
		        startActivityForResult(intent, VOICE_RECOGNITION_CODE);
			}
			
		});
								
		this.listening = (ImageButton)findViewById(R.id.listening);
		this.listening.setOnClickListener( new OnClickListener() {

			@Override
			public void onClick(View v) {
				textToSpeech.speak(answer.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
			}
			
		});

		if ( this.mode == LearnModeEnum.WRITING || this.mode == LearnModeEnum.RANDOM ) {
			this.handler = new Handler() {
		          public void  handleMessage(Message msg) {
					answer.setVisibility(View.VISIBLE);
					example.setVisibility(View.VISIBLE);
					commit.setVisibility(View.VISIBLE);		          
		          }
		     };			
		}
		
		this.randomModes = (List<LearnModeEnum>)extras.getSerializable(LEARN_RANDOM_LIST);
		this.repeat = extras.getBoolean(REPEAT_LEARN);
		
		List<Long> lessonIds = (List<Long>)extras.getSerializable( LESSONS_PARAM );		
		int wordsCount = extras.getInt(LEARN_CROSS_NUMBER, -1);
		this.idsToLearn = db.loadWordIds( lessonIds, wordsCount, this.mode, randomModes, repeat );
		this.historyId = db.addHistory(mode, randomModes);
								
		this.display();		
    }
	    	
	@Override
	protected void onDestroy() {
		db.close();
    	super.onDestroy();
	}
	
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    	if (requestCode == TEXT_TO_SPEECH_CODE) {
            if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {            	
            	textToSpeech = new TextToSpeech(this, this);            	
            } else {
                // missing data, install it
                Intent installIntent = new Intent();
                installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
                startActivity(installIntent);
            }
        } else if (requestCode == VOICE_RECOGNITION_CODE) {            
        	if ( intent != null ) {
	        	ArrayList<String> matches = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
	        	if ( matches != null ) {
		        	StringBuffer sb = new StringBuffer();
		        	for (String pron : matches) {
		        		sb.append(pron);
					}
	        		pronounce.setTextColor( res.getColor( R.color.pronounce_result ) );
		        	this.pronounce.setText( sb.toString() );
	        	} else {
	        		pronounce.setTextColor( res.getColor( R.color.pronounce_prompt ));
	        		pronounce.setText( R.string.pronounce_prompt );        		
	        	}
        	} else {
        		pronounce.setTextColor( res.getColor( R.color.pronounce_prompt ));
        		pronounce.setText( R.string.pronounce_prompt );        		
        	}
        }
    	super.onActivityResult(requestCode, resultCode, intent);
    }
	
	@Override
	public void onInit(int status) {
		Locale locale;
		String[] splited = settings.getString(ConfigActivity.LOCALE_PREFERENCE, "en_GB").split("_");
		if ( splited.length > 1 ) {
			locale = new Locale( splited[0], splited[1] );
		} else {
			locale = new Locale( splited[0] );
		}
		textToSpeech.setLanguage( locale );		
	}

    private void display() {
    	switch (this.state) {
		case QUESTION:
			this.doQuestion();
			break;
		case ANSWER:
			this.doAnswer();
			break;
		case HISTORY:			
			this.doHistory();
			break;
    	}
    }
    
    private void doQuestion() {
    	this.space.setVisibility(View.GONE);
		this.answer.setVisibility(View.GONE);
		this.example.setVisibility(View.GONE);
		this.commit.setVisibility(View.GONE);
		this.commit.setChecked(this.repeat);

		if ( this.idsToLearn.isEmpty() ) {
			counterText.setVisibility(View.INVISIBLE);
			
			this.writing.setVisibility(View.GONE);
			this.pronounce.setVisibility(View.GONE);
			this.listening.setVisibility(View.GONE);
			this.answer.setText( "" );				
			this.example.setText( "" );
			this.question.setText( getString( R.string.finish ) );
			
			int total = this.idsTouched.size();
			((TextView)findViewById(R.id.finish_total)).setText( "" + total);
			((TextView)findViewById(R.id.finish_passed)).setText( "" + passed);
			((TextView)findViewById(R.id.finish_failed)).setText( "" + (total - passed));
			finish.setVisibility( View.VISIBLE );
			
			this.state = LearnStateEnum.FINISH;
			return;
		}
					
		counterText.setText("" + this.idsToLearn.size());

		Random random = new Random(); 
		int index = random.nextInt( this.idsToLearn.size() );
		this.currentWordId = this.idsToLearn.get( index ); 			
		WordDTO word = db.fetchWord( this.currentWordId );
		this.currentLessonId = word.getLesson_id();			
				
		if ( this.mode == LearnModeEnum.RANDOM ) {
			this.target = this.recurseTarget( word, new ArrayList<LearnModeEnum>(this.randomModes) );
		}
		this.prepareDisplay( word );		
    }
    
    private LearnModeEnum recurseTarget(WordDTO word, List<LearnModeEnum> modes) {
		Random lottery = new Random();
		LearnModeEnum picked = modes.get( lottery.nextInt( modes.size() ) );  
		int modeval = this.repeat?0:1;
		switch (picked) {
			case QUESTIONING:
				if ( word.getQuestioning() == modeval ) {
					modes.remove(picked);
					return recurseTarget(word, modes);
				} else {
					return picked;
				}
			case ANSWERING:
				if ( word.getAnswering() == modeval ) {
					modes.remove(picked);
					return recurseTarget(word, modes);
				} else {
					return picked;
				}
			case WRITING:
				if ( word.getWriting() == modeval ) {
					modes.remove(picked);
					return recurseTarget(word, modes);
				} else {
					return picked;
				}
			case LISTENING:
				if ( word.getListening() == modeval ) {
					modes.remove(picked);
					return recurseTarget(word, modes);
				} else {
					return picked;
				}
			default:
				return LearnModeEnum.ANSWERING;
		}    	
    }
    
    private void prepareDisplay(WordDTO word) {
		String q, a;
		if ( this.target == LearnModeEnum.QUESTIONING ) {
			writing.setVisibility(View.GONE);
			pronounce.setVisibility(View.GONE);
			listening.setVisibility(View.GONE);
			
			q = word.getQuestion();
			a = word.getAnswer();
			foreignQuestion = true;
		} else if ( this.target == LearnModeEnum.WRITING ) {
			pronounce.setVisibility(View.GONE);
			if ( speechWriting ) {
				question.setVisibility(View.GONE);
				listening.setVisibility(View.VISIBLE);
				if ( textToSpeech != null ) {
					textToSpeech.speak(word.getQuestion(), TextToSpeech.QUEUE_FLUSH, null);
				}
			} else {			
				listening.setVisibility(View.GONE);
			}
			
			writing.setTextColor(res.getColor( R.color.learn_writing ));
			writing.setVisibility(View.VISIBLE);
			writing.setEnabled(true);
			writing.getText().clear();				
			InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
			mgr.showSoftInput(writing, InputMethodManager.SHOW_IMPLICIT);
			
			q = word.getAnswer();
			a = word.getQuestion();
			foreignQuestion = false;
		} else if ( this.target == LearnModeEnum.LISTENING ) {
			writing.setVisibility(View.GONE);
			pronounce.setVisibility(View.GONE);
			question.setVisibility(View.GONE);
			listening.setVisibility(View.VISIBLE);
			
			q = word.getAnswer();
			a = word.getQuestion();
			foreignQuestion = false;
			if ( textToSpeech != null ) {
				textToSpeech.speak(a, TextToSpeech.QUEUE_FLUSH, null);
			}
		} else if ( this.target == LearnModeEnum.PRONOUNCE ) {
			writing.setVisibility(View.GONE);
			listening.setVisibility(View.GONE);

			pronounce.setTextColor(res.getColor( R.color.pronounce_prompt ));
			pronounce.setVisibility(View.VISIBLE);
			pronounce.setText( R.string.pronounce_prompt );				
			
			q = word.getAnswer();
			a = word.getQuestion();
			foreignQuestion = false;
		} else {
			writing.setVisibility(View.GONE);
			pronounce.setVisibility(View.GONE);
			listening.setVisibility(View.GONE);

			q = word.getAnswer();
			a = word.getQuestion();
			foreignQuestion = false;			
		}
		
		this.question.setText( q );
		this.answer.setText( a );
		this.example.setText( word.getExample() );    			
    }
    
    private void doAnswer() {
    	this.touched++;
		this.idsTouched.add( 0, this.currentWordId );
		this.idsToLearn.remove( this.currentWordId );
		
		question.setVisibility(View.VISIBLE);		
		if ( target == LearnModeEnum.WRITING ) {
			if ( speechWriting ) {
				this.listening.setVisibility(View.GONE);
			}
			writing.setEnabled(false);
			InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
			if ( !mgr.hideSoftInputFromWindow(writing.getWindowToken(), 0, new KeyboardReciver(this.handler) ) ) {
				answer.setVisibility(View.VISIBLE);
				example.setVisibility(View.VISIBLE);
				commit.setVisibility(View.VISIBLE);		          				
			}
			
			String textWritten = writing.getText().toString();
			if ( textWritten != null && textWritten.equalsIgnoreCase( String.valueOf( answer.getText() ) ) ) {
				writing.setTextColor(res.getColor( R.color.writing_ok ));	
			} else {
				writing.setTextColor(res.getColor( R.color.writing_wrong ));
			}
		} else if ( target == LearnModeEnum.PRONOUNCE ) {
			String textPronouced = String.valueOf( pronounce.getText() );
			if ( textPronouced != null && textPronouced.equalsIgnoreCase( String.valueOf( answer.getText() ) ) ) {
				pronounce.setTextColor(res.getColor( R.color.writing_ok ));	
			} else {
				pronounce.setTextColor(res.getColor( R.color.writing_wrong ));
			}
			this.answer.setVisibility(View.VISIBLE);
			this.example.setVisibility(View.VISIBLE);
			this.commit.setVisibility(View.VISIBLE);				
		} else if ( target == LearnModeEnum.LISTENING ) {
			this.listening.setVisibility(View.GONE);
			this.answer.setVisibility(View.VISIBLE);
			this.example.setVisibility(View.VISIBLE);
			this.commit.setVisibility(View.VISIBLE);				
		} else {
			this.answer.setVisibility(View.VISIBLE);
			this.example.setVisibility(View.VISIBLE);
			this.commit.setVisibility(View.VISIBLE);				
		}
		this.space.setVisibility(View.INVISIBLE);
		
		if ( this.speechOnLearn && textToSpeech != null) {			
			if ( foreignQuestion ) {
				textToSpeech.speak(this.question.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
			} else {
				textToSpeech.speak(this.answer.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
			}
		}    	
    }
    
    private void doHistory() {
    	this.finish.setVisibility( View.GONE );
    	this.writing.setVisibility(View.GONE);
		this.pronounce.setVisibility(View.GONE);
		this.commit.setVisibility(View.GONE);
		this.space.setVisibility(View.GONE);

		this.currentWordId = this.idsTouched.get( this.touchedIndex );
		WordDTO history = db.fetchWord( this.currentWordId );
		if ( this.mode == LearnModeEnum.QUESTIONING ) {
			this.question.setText( history.getQuestion() );
			this.answer.setText( history.getAnswer() );
		} else {
			this.question.setText( history.getAnswer() );
			this.answer.setText( history.getQuestion() );				
		}
		this.example.setText( history.getExample() );
		this.answer.setVisibility(View.VISIBLE);
		this.example.setVisibility(View.VISIBLE);
    }
    
	private void onSwipeLeft() {
		if ( idsTouched.isEmpty() ) {
			return;
		}
		
		if ( this.state == LearnStateEnum.ANSWER ) {
			if ( touchedIndex < idsTouched.size() - 2 ) {
				touchedIndex+=2;
			} else {
				return;
			}
		} else {
			if ( touchedIndex < idsTouched.size() - 1 ) {
				touchedIndex++;
			}
		}
		
		this.state = LearnStateEnum.HISTORY;
		
		this.display();
	}

	private void onSwipeRight() {
		if ( touchedIndex >= 0 ) {
			touchedIndex--;
			if ( touchedIndex < 0 ) {
				this.state = LearnStateEnum.QUESTION;
			}
		} else if ( this.state == LearnStateEnum.QUESTION ) {
			this.state = LearnStateEnum.ANSWER;
		} else if ( this.state == LearnStateEnum.ANSWER ) {
			boolean checked = this.commit.isChecked();
			if ( checked ) {
				passed++;
			}
			
			db.wordLearned(this.currentWordId, this.currentLessonId, checked, this.target, this.historyId, this.touched, this.passed);
			this.state = LearnStateEnum.QUESTION;
		} else {
			this.state = LearnStateEnum.QUESTION;
		}
	
		this.display();
	}
    	
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev){		 
        super.dispatchTouchEvent(ev); 
        return gestureDetector.onTouchEvent(ev); 
    }
	
	private class LearnGestureListener extends GestureDetector.SimpleOnGestureListener {
		 private static final int SWIPE_MIN_DISTANCE = 120;
		 private static final int SWIPE_MAX_OFF_PATH = 250;
		 private static final int SWIPE_THRESHOLD_VELOCITY = 200;
		   
		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {
				return false;
			}
			if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
				onSwipeRight();
			} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
				onSwipeLeft();
			}	           
			return true;
		}
	}
	
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    	if ( this.settings.getBoolean(ConfigActivity.SPEECH_PREFERENCE, false) ) {
	    	MenuInflater inflater = getMenuInflater();
	        inflater.inflate(R.menu.learn_menu, menu);
	        return true;
    	}
    	
    	if (  this.settings.getBoolean( ConfigActivity.SPEECH_ONLEARN_PREFERENCE, false) ) {
    		menu.findItem(R.id.menu_auto_speech).setTitle( R.string.menu_auto_speech_off );
    	} else {
    		menu.findItem(R.id.menu_auto_speech).setTitle( R.string.menu_auto_speech_on );
    	}        
    	
    	return super.onCreateOptionsMenu(menu);   
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
        	case R.id.menu_auto_speech:
        		boolean auto = this.settings.getBoolean( ConfigActivity.SPEECH_ONLEARN_PREFERENCE, false);
        		Editor edit = this.settings.edit();
        		edit.putBoolean(ConfigActivity.SPEECH_ONLEARN_PREFERENCE, !auto);
        		edit.commit();
        		        		
        		speechOnLearn = !auto;        		
        		item.setTitle( speechOnLearn?R.string.menu_auto_speech_off:R.string.menu_auto_speech_on );        		
        		break;                                
        	case R.id.menu_edit_word:
        		this.showDialog(EDIT_WORD_DIALOG);
        		break;
        		
        }
        
        return super.onOptionsItemSelected(item);
    }

    @Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case EDIT_WORD_DIALOG:
			View wordEditView = LayoutInflater.from(this).inflate(R.layout.word_dialog, null); 

            final EditText questionEdit = (EditText)wordEditView.findViewById( R.id.word_question );
            final EditText answerEdit = (EditText)wordEditView.findViewById( R.id.word_answer );
            final EditText exampleEdit = (EditText)wordEditView.findViewById( R.id.word_example );

			return new AlertDialog.Builder(this)
			.setTitle(R.string.word_edit_title)
			.setView(wordEditView)
			.setPositiveButton(R.string.btn_save, new DialogInterface.OnClickListener() {
			     public void onClick(DialogInterface dialog, int whichButton) {
                     String qvalue = questionEdit.getText().toString();                     
                     String avalue = answerEdit.getText().toString();
                     String evalue = exampleEdit.getText().toString();
                     if ( qvalue == null || qvalue.length() == 0 || avalue == null || avalue.length() == 0 ) {
                    	 Toast.makeText(LearnActivity.this, R.string.word_invalid, Toast.LENGTH_LONG).show();
                     } else {                     
                    	 onWordChanged( qvalue, avalue, evalue );
                     }
			     }
			 })
			.setNegativeButton(R.string.btn_cancel, null)
			.create();     				
		default:
	    	return super.onCreateDialog(id);
		}
	}
    
    private void onWordChanged( String question, String answer, String example ) {
    	db.updateWord( currentWordId, currentLessonId, question, answer, example );
		if ( this.foreignQuestion ) {
	    	this.question.setText( question );
			this.answer.setText( answer );			
		} else {
	    	this.question.setText( answer );
			this.answer.setText( question );			
		}
		this.example.setText( example );    			    	
    }
    
    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        super.onPrepareDialog(id, dialog);

        EditText question = (EditText)dialog.findViewById( R.id.word_question );        
        EditText answer = (EditText)dialog.findViewById( R.id.word_answer );
        EditText example = (EditText)dialog.findViewById( R.id.word_example );
        switch (id) {
            case EDIT_WORD_DIALOG:
            	question.requestFocus();
            	WordDTO word = db.fetchWord( this.currentWordId );
            	question.setText(word.getQuestion());
            	answer.setText(word.getAnswer());
            	example.setText(word.getExample());
            	break;            	
        }
    }

    private class KeyboardReciver extends ResultReceiver {

    	private Handler myHandler; 
    	
    	public KeyboardReciver(Handler handler) {
			super(null);
			this.myHandler = handler; 
		}

		@Override
		protected void onReceiveResult(int resultCode, Bundle resultData) {
			super.onReceiveResult(resultCode, resultData);
			myHandler.sendEmptyMessage(0);
		}
    	    	
    }

}

